package com.personal.dataanalyse.reportmanage.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.personal.core.data.DataColumn;
import com.personal.core.data.DataRow;
import com.personal.core.data.DataTable;
import com.personal.core.utils.CoreUtil;
import com.personal.core.utils.ReGularUtil;
import com.personal.dataanalyse.enums.AdditionalTypeEnum;
import com.personal.dataanalyse.enums.FieldDataTypeEnum;
import com.personal.dataanalyse.enums.ModelFieldTypeEnum;
import com.personal.dataanalyse.reportmanage.entity.ConditionInfo;
import com.personal.dataanalyse.reportmanage.entity.DataColumnReportEx;
import com.personal.dataanalyse.reportmanage.entity.DataRowReportEx;

/**
 * 模型结果校验
 * @author cuibo
 *
 */
public final class ModelResultValidator
{
    private ModelResultValidator()
    {
    }
    
    /**
     * 对模型结果格式化
     * @param transRowCol
     * @param modelResult
     */
    public static void formatModelResultData(boolean transRowCol, DataTable modelResult)
    {
        if (!CoreUtil.checkDataTableHasData(modelResult))
        {
            return;
        }
        if (transRowCol)
        {
            for (DataRow row : modelResult.getRows())
            {
                DataRowReportEx rowEx = (DataRowReportEx) row;
                if (!FieldDataTypeEnum.数值.toString().equals(rowEx.getDataType()))
                {
                    continue;
                }
                for (DataColumn column : modelResult.getColumns())
                {
                    DataColumnReportEx columnEx = (DataColumnReportEx) column;
                    if (columnEx.isOtherGroupCol())
                    {
                        continue;
                    }
                    row.getItemMap().put(column.getColumnName(), CoreUtil.parseDblStr(row.getItemMap().get(column.getColumnName()), rowEx.getDotNum(), true));
                }
            }
        } else
        {
            for (DataColumn column : modelResult.getColumns())
            {
                DataColumnReportEx colEx = (DataColumnReportEx) column;
                if (FieldDataTypeEnum.数值.toString().equals(colEx.getDataType()))
                {
                    for (DataRow row : modelResult.getRows())
                    {
                        row.getItemMap().put(column.getColumnName(), CoreUtil.parseDblStr(row.getItemMap().get(column.getColumnName()), colEx.getDotNum(), true));
                    }
                }
            }
        }
    }

    /**
     * 对模型的构建结果进行数据类型校验和表达式校验
     * @param transRowCol   是否是行列转换
     * @param modelResult   必须通过modelBuilder构建出来的DataTable
     * @return  key:行号,列号  value:对应的校验规则
     */
    public static Map<String, String> validateModelResultData(boolean transRowCol, DataTable modelResult)
    {
        // 没有数据，则不需要校验
        if (!CoreUtil.checkDataTableHasData(modelResult))
        {
            return null;
        }
        if (transRowCol)
        {
            // 校验行列转换的DataTable  指标定义在DataRow上
            return validateTransRowColDataTable(modelResult);
        }
        // 校验非行列转换的DataTable  指标定义在DataColumn上
        return validateNotTransRowColDataTable(modelResult);
    }

    private static Map<String, String> validateNotTransRowColDataTable(DataTable modelResult)
    {
        Map<String, String> result = new HashMap<String, String>();
        
        Map<DataColumn, Object> max = new HashMap<DataColumn, Object>();
        Map<DataColumn, Object> min = new HashMap<DataColumn, Object>();
        Map<DataColumn, Object> hj = new HashMap<DataColumn, Object>();
        
        // 将列存入Map，方便后面的表达式校验  key:fieldDataSql
        Map<String, List<DataColumnReportEx>> map = new HashMap<String, List<DataColumnReportEx>>();
        for (DataColumn col : modelResult.getColumns())
        {
            DataColumnReportEx colEx = (DataColumnReportEx) col;
            List<DataColumnReportEx> localList = map.get(colEx.getFieldDataSql());
            if (localList == null)
            {
                localList = new ArrayList<DataColumnReportEx>();
                map.put(colEx.getFieldDataSql(), localList);
            }
            localList.add(colEx);
        }
        
        String myValue = null;
        String thisValue = null;
        int colIndex = -1;
        
        sign:
        for (DataColumn column : modelResult.getColumns())
        {
            colIndex ++;
            DataColumnReportEx columnEx = (DataColumnReportEx) column;
            // 其他维度列，不需要校验，其属于格式校验 
            if (columnEx.isOtherGroupCol())
            {
                continue;
            }
            // 精确度
            double precision = getPrecision(columnEx.getDotNum());
            int rowIndex = -1;
            int pjCount = 0;
            for (DataRow row : modelResult.getRows())
            {
                DataRowReportEx rowEx = (DataRowReportEx) row;
                rowIndex ++;
                
                // 空值不参与平均值的计算
                if (!rowEx.isAdditionalRow() && !CoreUtil.isEmpty(rowEx.getItemMap().get(column.getColumnName())))
                {
                    pjCount ++;
                }
                thisValue = CoreUtil.parseDblStr(row.getItemMap().get(column.getColumnName()), columnEx.getDotNum());
                //  附加行,计算列不用附加列的方式处理
                if (rowEx.isAdditionalRow() && !ModelFieldTypeEnum.isCalField(columnEx.getFieldType()))
                {
                    // 附加列
                    AdditionalTypeEnum additionalType = rowEx.getAdditionalType();
                    switch (additionalType)
                    {
                    case 求和:
                        myValue = CoreUtil.parseDblStr(hj.get(column), columnEx.getDotNum());
                        if (CoreUtil.compareNumber(myValue, thisValue, precision) != 0)
                        {
                            result.put(rowIndex + "," + colIndex, "期望值为：" + myValue + "！");
                            continue;
                        }
                        break;
                    case 求平均:
                        myValue = CoreUtil.parseDblStr(CoreUtil.divideWithNull(hj.get(column), pjCount), columnEx.getDotNum());
                        if (CoreUtil.compareNumber(myValue, thisValue, precision) != 0)
                        {
                            result.put(rowIndex + "," + colIndex, "期望值为：" + myValue + "！");
                            continue;
                        }
                        break;
                    case 求最大值:
                        myValue = CoreUtil.parseDblStr(max.get(column), columnEx.getDotNum());
                        if (CoreUtil.compareNumber(myValue, thisValue, precision) != 0)
                        {
                            result.put(rowIndex + "," + colIndex, "期望值为：" + myValue + "！");
                            continue;
                        }
                        break;
                    case 求最小值:
                        myValue = CoreUtil.parseDblStr(min.get(column), columnEx.getDotNum());
                        if (CoreUtil.compareNumber(myValue, thisValue, precision) != 0)
                        {
                            result.put(rowIndex + "," + colIndex, "期望值为：" + myValue + "！");
                            continue;
                        }
                        break;
                    default:
                        break;
                    }
                    continue;
                }
                FieldDataTypeEnum dataType = FieldDataTypeEnum.valueOf(columnEx.getDataType());
                if (!dataType.validateObjectType(row.getItemMap().get(column.getColumnName())))
                {
                    result.put(rowIndex + "," + colIndex, "该值应为" + dataType.toString() + "类型！");
                    continue;
                }
                // 数值计算出最值和合计值
                switch (dataType)
                {
                case 数值:
                    // 忽略空值
                    if (!CoreUtil.isEmpty(row.getItemMap().get(columnEx.getColumnName())))
                    {
                        min.put(column, (!min.containsKey(column) || CoreUtil.compareNumber(row.getItemMap().get(column.getColumnName()), min.get(column)) < 0) ? row.getItemMap().get(column.getColumnName()) : min.get(column));
                        max.put(column, (!max.containsKey(column) || CoreUtil.compareNumber(row.getItemMap().get(column.getColumnName()), max.get(column)) > 0) ? row.getItemMap().get(column.getColumnName()) : max.get(column));
                        hj.put(column, CoreUtil.addWithNull(hj.get(column), row.getItemMap().get(column.getColumnName())));
                    }
                    break;
                case 日期:
                    if (!CoreUtil.isEmpty(row.getItemMap().get(columnEx.getColumnName())))
                    {
                        min.put(column, (!min.containsKey(column) || CoreUtil.compareDate(row.getItemMap().get(column.getColumnName()), min.get(column)) < 0) ? row.getItemMap().get(column.getColumnName()) : min.get(column));
                        max.put(column, (!max.containsKey(column) || CoreUtil.compareDate(row.getItemMap().get(column.getColumnName()), max.get(column)) > 0) ? row.getItemMap().get(column.getColumnName()) : max.get(column));
                    }
                    break;
                default:
                    break;
                }
                // 计算列
                if (ModelFieldTypeEnum.isCalField(columnEx.getFieldType()))
                {
                    myValue = CoreUtil.parseDblStr(CoreUtil.calculateExByAviWithAbs(columnEx.getFieldDataSql(), row.getItemMap()), columnEx.getDotNum());
                    if (CoreUtil.compareNumber(myValue, thisValue, precision) != 0)
                    {
                        result.put(rowIndex + "," + colIndex, "期望值为：" + myValue + "！");
                    }
                    continue;
                }
                // 表达式
                if (columnEx.isExpType())
                {
                    // 检查有没有对应的表达式行
                    String[] arr = ReGularUtil.EXPANDKHPATTERNANDABS.split(columnEx.getFieldDataSql());
                    Map<String, Object> calMap = new HashMap<String, Object>();
                    for (String string : arr)
                    {
                        if (ModelUtil.checkLegalFieldDataSql(string))
                        {
                            // 该表没有定义这个表达式的列，则没办法校验
                            if (!map.containsKey(string))
                            {
                                continue sign;
                            }
                            DataColumn sameConditionCol = querySameConditionCol(columnEx.getConditions(), map.get(string));
                            // 该表没有定义这个表达式的相同条件列，则没办法校验
                            if (sameConditionCol == null)
                            {
                                continue sign;
                            }
                            calMap.put(string, row.getItemMap().get(sameConditionCol.getColumnName()));
                        }
                    }
                    // 计算出值
                    myValue = CoreUtil.parseDblStr(CoreUtil.calculateExByAviWithAbs(columnEx.getFieldDataSql(), calMap), columnEx.getDotNum());
                    if (CoreUtil.compareNumber(myValue, thisValue, precision) != 0)
                    {
                        result.put(rowIndex + "," + colIndex, "期望值为：" + myValue + "！");
                    }
                    continue;
                }
            }
        }
        return result;
    }

    private static Map<String, String> validateTransRowColDataTable(DataTable modelResult)
    {
        Map<String, String> result = new HashMap<String, String>();
        
        Map<DataRow, Object> max = new HashMap<DataRow, Object>();
        Map<DataRow, Object> min = new HashMap<DataRow, Object>();
        Map<DataRow, Object> hj = new HashMap<DataRow, Object>();
        // 将行存入Map，方便后面的表达式校验  key:fieldDataSql
        Map<String, List<DataRowReportEx>> map = new HashMap<String, List<DataRowReportEx>>();
        for (DataRow row : modelResult.getRows())
        {
            DataRowReportEx rowEx = (DataRowReportEx) row;
            List<DataRowReportEx> localList = map.get(rowEx.getFieldDataSql());
            if (localList == null)
            {
                localList = new ArrayList<DataRowReportEx>();
                map.put(rowEx.getFieldDataSql(), localList);
            }
            localList.add(rowEx);
        }
        // 非附加列列数
        String myValue = null;
        String thisValue = null;
        
        int rowIndex = -1;
        sign:
        for (DataRow row : modelResult.getRows())
        {
            rowIndex ++;
            DataRowReportEx rowEx = (DataRowReportEx) row;
            
            // 精确度
            double precision = getPrecision(rowEx.getDotNum());
            // 平均的个数
            int pjCount = 0;
            int colIndex = -1;
            for (DataColumn column : modelResult.getColumns())
            {
                colIndex ++;
                DataColumnReportEx columnEx = (DataColumnReportEx) column;
                // 其他维度列，不需要校验，其属于格式校验 
                if (columnEx.isOtherGroupCol())
                {
                    continue;
                }
                // 空值不参与平均值的计算
                if (!columnEx.isAdditionalCol() && !CoreUtil.isEmpty(row.getItemMap().get(columnEx.getColumnName())))
                {
                    pjCount ++;
                }
                thisValue = CoreUtil.parseDblStr(row.getItemMap().get(column.getColumnName()), rowEx.getDotNum());
                // 附加列,计算行不用附加列的方式校验
                if (columnEx.isAdditionalCol() && !ModelFieldTypeEnum.isCalField(rowEx.getFieldType()))
                {
                    AdditionalTypeEnum additionalType = columnEx.getAdditionalType();
                    switch (additionalType)
                    {
                    case 求和:
                        myValue = CoreUtil.parseDblStr(hj.get(row), rowEx.getDotNum());
                        if (CoreUtil.compareNumber(myValue, thisValue, precision) != 0)
                        {
                            result.put(rowIndex + "," + colIndex, "期望值为：" + myValue + "！");
                            continue;
                        }
                        break;
                    case 求平均:
                        myValue = CoreUtil.parseDblStr(CoreUtil.divideWithNull(hj.get(row), pjCount), rowEx.getDotNum());
                        if (CoreUtil.compareNumber(myValue, thisValue, precision) != 0)
                        {
                            result.put(rowIndex + "," + colIndex, "期望值为：" + myValue + "！");
                            continue;
                        }
                        break;
                    case 求最大值:
                        myValue = CoreUtil.parseDblStr(max.get(row), rowEx.getDotNum());
                        if (CoreUtil.compareNumber(myValue, thisValue, precision) != 0)
                        {
                            result.put(rowIndex + "," + colIndex, "期望值为：" + myValue + "！");
                            continue;
                        }
                        break;
                    case 求最小值:
                        myValue = CoreUtil.parseDblStr(min.get(row), rowEx.getDotNum());
                        if (CoreUtil.compareNumber(myValue, thisValue, precision) != 0)
                        {
                            result.put(rowIndex + "," + colIndex, "期望值为：" + myValue + "！");
                            continue;
                        }
                        break;
                    default:
                        break;
                    }
                    continue;
                }
                FieldDataTypeEnum dataType = FieldDataTypeEnum.valueOf(rowEx.getDataType());
                if (!dataType.validateObjectType(row.getItemMap().get(column.getColumnName())))
                {
                    result.put(rowIndex + "," + colIndex, "该值应为" + dataType.toString() + "类型！");
                    continue;
                }
                // 数值计算出最值和合计值
                switch (dataType)
                {
                case 数值:
                    min.put(row, (!min.containsKey(row) || CoreUtil.compareNumber(row.getItemMap().get(column.getColumnName()), min.get(row)) < 0) ? row.getItemMap().get(column.getColumnName()) : min.get(row));
                    max.put(row, (!max.containsKey(row) || CoreUtil.compareNumber(row.getItemMap().get(column.getColumnName()), max.get(row)) > 0) ? row.getItemMap().get(column.getColumnName()) : max.get(row));
                    hj.put(row, CoreUtil.addWithNull(hj.get(row), row.getItemMap().get(column.getColumnName())));
                    break;
                case 日期:
                    min.put(row, (!min.containsKey(row) || CoreUtil.compareDate(row.getItemMap().get(column.getColumnName()), min.get(row)) < 0) ? row.getItemMap().get(column.getColumnName()) : min.get(row));
                    max.put(row, (!max.containsKey(row) || CoreUtil.compareDate(row.getItemMap().get(column.getColumnName()), max.get(row)) > 0) ? row.getItemMap().get(column.getColumnName()) : max.get(row));
                    break;
                default:
                    break;
                }
                // 计算列
                if (ModelFieldTypeEnum.isCalField(rowEx.getFieldType()))
                {
                    Map<String, Object> calMap = new HashMap<String, Object>();
                    List<DataColumnReportEx> cals = rowEx.getColumnReportEx().getCalColumns();
                    for (DataColumnReportEx cal : cals)
                    {
                        calMap.put(cal.getColumnName(), cal.getRowReportEx().getItemMap().get(column.getColumnName()));
                    }
                    myValue = CoreUtil.parseDblStr(CoreUtil.calculateExByAviWithAbs(rowEx.getFieldDataSql(), calMap), rowEx.getDotNum());
                    if (CoreUtil.compareNumber(myValue, thisValue, precision) != 0)
                    {
                        result.put(rowIndex + "," + colIndex, "期望值为：" + myValue + "！");
                    }
                    continue;
                }
                // 表达式
                if (rowEx.isExpType())
                {
                    // 检查有没有对应的表达式行
                    String[] arr = ReGularUtil.EXPANDKHPATTERNANDABS.split(rowEx.getFieldDataSql());
                    Map<String, Object> calMap = new HashMap<String, Object>();
                    for (String string : arr)
                    {
                        if (ModelUtil.checkLegalFieldDataSql(string))
                        {
                            // 该表没有定义这个表达式的列，则没办法校验
                            if (!map.containsKey(string))
                            {
                                continue sign;
                            }
                            DataRow sameConditionRow = querySameConditionRow(rowEx.getConditions(), map.get(string));
                            // 该表没有定义这个表达式的相同条件列，则没办法校验
                            if (sameConditionRow == null)
                            {
                                continue sign;
                            }
                            calMap.put(string, sameConditionRow.getItemMap().get(columnEx.getColumnName()));
                        }
                    }
                    // 计算出值
                    myValue = CoreUtil.parseDblStr(CoreUtil.calculateExByAviWithAbs(rowEx.getFieldDataSql(), calMap), rowEx.getDotNum());
                    if (CoreUtil.compareNumber(myValue, thisValue, precision) != 0)
                    {
                        result.put(rowIndex + "," + colIndex, "期望值为：" + myValue + "！");
                    }
                    continue;
                }
            }
        }
        return result;
    }

    /**
     * 从list中查找和conditions条件一致的DataRow
     * @param conditions
     * @param list
     * @return
     */
    private static DataRow querySameConditionRow(List<ConditionInfo> conditions, List<DataRowReportEx> list)
    {
        if (CoreUtil.isEmpty(list))
        {
            return null;
        }
        if (CoreUtil.isEmpty(conditions))
        {
            return list.get(0);
        }
        for (DataRowReportEx dataRowReportEx : list)
        {
            if (checkSameCondition(conditions, dataRowReportEx.getConditions()))
            {
                return dataRowReportEx;
            }
        }
        return null;
    }
    
    private static DataColumn querySameConditionCol(List<ConditionInfo> conditions, List<DataColumnReportEx> list)
    {
        if (CoreUtil.isEmpty(list))
        {
            return null;
        }
        if (CoreUtil.isEmpty(conditions))
        {
            return list.get(0);
        }
        for (DataColumnReportEx dataColumnReportEx : list)
        {
            if (checkSameCondition(conditions, dataColumnReportEx.getConditions()))
            {
                return dataColumnReportEx;
            }
        }
        return null;
    }

    private static boolean checkSameCondition(List<ConditionInfo> one, List<ConditionInfo> two)
    {
        if (CoreUtil.isEmpty(one) && CoreUtil.isEmpty(two))
        {
            return true;
        }
        List<ConditionInfo> tempOne = new ArrayList<ConditionInfo>(one);
        List<ConditionInfo> tempTwo = new ArrayList<ConditionInfo>(two);
        for (Iterator<ConditionInfo> iteratorOne = tempOne.iterator(); iteratorOne.hasNext();)
        {
            ConditionInfo localOne = iteratorOne.next();
            if (CoreUtil.isEmpty(localOne.getRelation()))
            {
                iteratorOne.remove();
                continue;
            }
            for (Iterator<ConditionInfo> iteratorTwo = tempTwo.iterator(); iteratorTwo.hasNext();)
            {
                ConditionInfo localTwo = iteratorTwo.next();
                if (CoreUtil.isEmpty(localTwo.getRelation()))
                {
                    iteratorTwo.remove();
                    continue;
                }
                if (localOne.customEquals(localTwo))
                {
                    iteratorOne.remove();
                    iteratorTwo.remove();
                    break;
                }
            }
        }
        return tempOne.isEmpty() && tempTwo.isEmpty();
    }
    
    /**
     * 获取精确度
     * @param dotNum
     * @return
     */
    private static double getPrecision(int dotNum)
    {
        double result = 1;
        for (int i = 0; i < dotNum; i++)
        {
            result /= 10;
        }
        return result;
    }
    
}
